/*
* Copyright (c) 2010 Michael Collins
*
* This file is part of TerraFirma.
*
* TerraFirma is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TerraFirma is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TerraFirma.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPI_H
#define MPI_H

#include <queue>
#include <map>

#include "message_header.h"
#include "message_queue.h"
#include "config.h"
//#include "network_pipe.h"
//Message Passing Interface
namespace engine
{
    //template <typename T = message_header>
    //class task_thread;

    //template <typename T = message_header>
    //class task_thread<T>::message_queue;

    class network_pipe;

    class mpi
    {
    public:
        mpi(): netpipe(NULL) {}
        ~mpi();

        /**Methods to enqueue a message onto another task's message queue**/
        //Enqueue a message for a task on the local host
        void enqueue_local(const message_header* msg);

        //Enqueue a message on an arbitrary host across the network
        //(This always go through the server, even if dst_host is the local host)
        //Broadcast messages go to host 255
        void enqueue_remote(hostid dst_host, const message_header* msg);

        //Enqueue a message to be processed by the server
        void enqueue_server(const message_header* msg);

        //Enqueue a message to be processed by both the local host and server
        void enqueue_shared(const message_header* msg);

        /*Call with the specific queue that will act as a pipe for network-bound messages*/
        void bind_netpipe(network_pipe* pipe) {netpipe = pipe;}

        /*Broadcasts a very simple message (strictly of header type) to all local tasks*/
        void broadcast_local(const message_header* msg);
        void broadcast_close();
        void broadcast_pause();

        //Server messages to all hosts! Do not call these unless you are a server
        #ifdef SERVING
        void enqueue_all_remote_hosts(const message_header* msg);
        //Sends the message to every remote host EXCEPT the host indicated by excpt_host
        void enqueue_all_other_hosts(hostid expt_host, const message_header* msg);
        #endif

    //protected:
        //Add and remove support for message passing
        bool add_listener(message_queue<>* queue, const task_id tid);
        bool remove_listener(const task_id tid);

        #ifdef SERVING
        void mpi::enqueue_all_remote_hosts(const message_header* msg);
        void mpi::enqueue_all_other_hosts(hostid expt_host, const message_header* msg);
        #endif
    private:
        //A helper function to 1: determine if tid is actually a current listener, and
        //2: get a pointer to that task's queue if it exists
        bool get_task(task_id tid, message_queue<>** const ptr);
        message_queue<>* get_task(task_id tid);
        bool get_task_exists(task_id tid);

        /**Base methods to add the message to local or remote task queues**/
        //void enqueue_local_m(const message_header* msg);
        //void enqueue_remote_m(hostid dest_host, const message_header* msg);

        std::map<task_id, message_queue<>*> listeners;
        network_pipe* netpipe; //The queue to send outward-bound messages on
    };

};
#endif // MPI_H
